package com.my.study.utils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 参考博客：https://howtodoinjava.com/java8/stream-distinct-by-multiple-fields/#:~:text=Distinct%20by%20multiple%20fields%20%E2%80%93%20distinctByKeys%20%28%29%20function,the%20values%20of%20fields%20to%20check%20distinct%20combinations.
 *
 * @author: yidujun
 * @create: 2021/12/20 16:24
 */
public final class StreamDistinctUtils {


    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    @SafeVarargs
    public static <T> Predicate<T> distinctByKeys(Function<? super T, ?>... keyExtractors) {
        final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();

        return t -> {
            final List<?> keys = Arrays.stream(keyExtractors)
                    .map(ke -> ke.apply(t))
                    .collect(Collectors.toList());

            return seen.putIfAbsent(keys, Boolean.TRUE) == null;
        };
    }


    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        list.add(new Student(now.minusDays(1), "123456", 24));
        list.add(new Student(now.minusDays(1), "123456", 24));
        list.add(new Student(now.minusDays(2), "1234567", 25));

        List<Student> students = list.stream().filter(distinctByKeys(Student::getStartTime, Student::getPassword))
                .collect(Collectors.toList());

        System.out.println(students);

    }

    static class Student {
        LocalDateTime startTime;
        String password;
        Integer age;

        public Student() {
        }

        public Student(LocalDateTime startTime, String password, Integer age) {
            this.startTime = startTime;
            this.password = password;
            this.age = age;
        }

        public LocalDateTime getStartTime() {
            return startTime;
        }

        public void setStartTime(LocalDateTime startTime) {
            this.startTime = startTime;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }

}
